tree grammar TBTJcompiler;

options {
  language = Java;
  tokenVocab = TBTParser;
  ASTLabelType = CommonTree;
  output=template;
}

@header {
  package compiler;
  import java.util.TreeSet;
}

@members {
  TreeSet<String> ctors = new TreeSet<String>();
}



prog
scope{ int tmp_no;}
@init{ $prog::tmp_no = 0;}
    : ^(PROG prolog ^(TL_BINDINGS (b+=tl_bind)*) ^(TERM term))
    ->
    main_class(term={$term.st}, prolog={$prolog.st}, tmp_no={$prog::tmp_no}, bindings={$b})
    ;
    
tl_bind
    : ^(TL_BINDING ID . term) -> tlbind(name={$ID.text}, term={$term.st});
/* Generate class definitions for each datatype */  
prolog   : ^(PROLOG (d+=datatype)*) -> concat(elems = {$d});
datatype : ^(DATATYPE . . tconss)   -> id(x = {$tconss.st});
tconss
scope{int c_no;} 
@init{($tconss::c_no) = 0;}
    : ^(TCONSTRUCTORS (c+=constr)*) -> concat(elems = {$c});
constr
@after{($tconss::c_no)++;} 
    : ^(TCONS (ID {ctors.add($ID.text);}) .) -> type_con(name = {$ID.text}, no={ ($tconss::c_no) }) ;

/* Generate code for the lambda term */
term   
    : tletrec   -> id(x={$tletrec.st})
    | tcase_of  -> id(x={$tcase_of.st})
    | ttype_abs -> id(x={$ttype_abs.st})
    | ttype_app -> id(x={$ttype_app.st})
    | tlam_abs  -> id(x={$tlam_abs.st})
    | tlam_app  -> id(x={$tlam_app.st})
    | tvar      -> id(x={$tvar.st})
    ;

/*
 * Type application and abstractions are translated to dummy lambda application
 * and abstractions in order to avoid evaluation of type abstractions in a lazy setting    
 */
ttype_abs : ^(TYPE_ABS . term)         -> lambda(body={$term.st});     
ttype_app : ^(TYPE_APP t1=term .)      -> app(fun={$t1.st}); 
tlam_abs  : ^(LAM_ABS ID . term )      -> lambda(var={$ID.text}, body={$term.st});
tlam_app  : ^(LAM_APP t1=term t2=term) -> app(fun={$t1.st}, arg={$t2.st});
// data constructors are reserved and cannot be used as variable identifier
tvar      : ^(VAR ID)                  -> {!ctors.contains($ID.text)}? var(x = {$ID.text})
                                       -> constr(c = {$ID.text})
          ;
/*
 * Case analisys is implemented with the ternaru operator x?y:z in order to avoid 
 * the evaluation of all branches before the expression is evaluated.
 */
tcase_of  
@init{ int crt_tmp_no = $prog::tmp_no++; }
    : ^(CASE . term branches[crt_tmp_no]) -> case_of(tmp_idx={crt_tmp_no}, exp={$term.st}, fn_list={$branches.st})
    ;
branches [int tmp]
@init{ int br_no = 0; }
    : ^(BRANCHES (b+= branch[tmp, br_no++]*))   -> branches(fns={$b})
    ;
branch [int tmp, int br]
    : ^(BRANCH ID term) -> branch(tmp_idx={tmp}, idx={br}, fn={$term.st}, comment={$ID.text})
    ;

/*
 * Letrec construction should be translated to a recursive function call.
 */
tletrec   
    : ^(LETREC . ID term)          -> letrec(name={$ID.text}, exp={$term.st})
    ;
